///
//  Anime v 1.0
//  Russell Lowke, Febrauary 27th 2011
//
//  Copyright (c) 2011 Lowke Media
//  see http://www.lowkemedia.com for more information
//
//  Permission is hereby granted, free of charge, to any person obtaining a 
//  copy of this software and associated documentation files (the "Software"), 
//  to deal in the Software without restriction, including without limitation 
//  the rights to use, copy, modify, merge, publish, distribute, sublicense, 
//  and/or sell copies of the Software, and to permit persons to whom the 
//  Software is furnished to do so, subject to the following conditions:
// 
//  The above copyright notice and this permission notice shall be included in 
//  all copies or substantial portions of the Software.
// 
//  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 
//  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
//  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 
//  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 
//  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 
//  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 
//  IN THE SOFTWARE. 
//
//

var Anime = function(animatem,          // (Animatem) Controlling animator
                     target)            // (object) Target being animatedp
{
    this.animatem = animatem;           // (Animatem) controlling animatem object
    this.target = target;               // (object) target associated with this anime
    this.effectsList = new Array();     // (array) list of Effects
    this.effectsDict = new Object();    // (object) dictionary of effects keyed by name for fast lookup
    this.persist = false                // (boolean) if true then anime is not automatically removed when without effects


    this.update = function (updateTime) 
    {
        /*
        // if there are no effects on anime then remove the anime
        if (this.effectsList.length === 0 && ! this.persist) {
            // remove();
        }
        */
        
        // iterate through effects
        for (var i = 0; i < this.effectsList.length; ++i) {
            this.effectsList[i].resolve(updateTime);
        }
    }
    
    //
    // helper methods for addFX()
    
    this.addEffect = function(effect, funct, args, obj) {
        return this.addFX({effect:effect, name:Anime.REPLACE, funct:funct, args:args, obj:obj});
    }
    
    this.nameEffect = function(effect, name, funct, args, obj) {
        if (! name) {
            name = Anime.AUTO_RENAME;
        }
        return this.addFX({effect:effect, name:name, funct:funct, args:args, obj:obj});
    }
    
    // UPDATE TO HOW THIS IS DEATH WITH UNDER Anime.as
    this.addFX = function(objParams)
    {
        // collect parameters
        var effect = objParams.effect;      // (BaseFx) effect being added to this Anime
        var name    = objParams.name;       // (string) name to add effect as
        var funct   = objParams.funct;      // (function) function callback to call when effect finishes
        var args    = objParams.args;       // (array) arguments to pass to function callback
        var obj     = objParams.obj;        // (object) object to pass function callback to.  NEEDED?
        
        // rename effect before adding it to the anime list
        //  Note: renaming efects allows you to stack effects of the same type
        if (name && 
            name !== Anime.REPLACE && 
            name !== Anime.AUTO_RENAME) {
            effect.name = name;
        }
        
        // if effect with this name already exists then...
        var existingEffect = this.getEffect(effect.name, false);
        if (existingEffect) {
            if (name === Anime.REPLACE) {
                LogAs.debug("Effect \"" + effect.name + "\" being replaced on anime " + this.target, Animatem.DEBUG_EFFECT_BEING_REPLACED);
                
                // ...remove existing effect so no conflict occurs
                this.removeEffect(existingEffect);
                
            } else if (name === Anime.AUTO_RENAME) {
                
                // ...rename effect so no conflict occurs
                var counter = 1;
                var newName;
                do {
                    ++counter;
                    newName = effect.name + "_" + counter;
                } while (this.getEffect(newName, false));
                effect.name = newName;
                
            } else {
                
                // can't replace existing effect
                throw new Error("Effect \"" + effect.name + "\" already exists on anime.\r");

                return;
            }
        }
        
        this.effectsList.push(effect);
        this.effectsDict[effect.name] = effect;
        effect.initialize(this.animatem, this, this.target, new Date().getTime());
        
        if (funct) {
            $(effect).bind(BaseFx.EFFECT_DONE_EVENT, function() {
                funct.apply(obj, args)
            });
        }
        
        return effect;
        
    }
    
    //
    // get effect from effects list by name
    this.getEffect = function(effectName,           // (string) name of effect being searched for
                              giveWarning)          // (boolean) if true warning given if effect not found
    {
        var effect = this.effectsDict[effectName];
        if (effect) {
            return effect;
        } else if (giveWarning) {
            LogAs.warning("Could not find effect \"" + effectName + "\" on " + this.target + ".\r" +
                "Effects that were found were the following: " + dumpEffects() + ".", Animatem.WARNING_CANT_FIND_EFFECT);
        }
        
        return;
    }
    
    
    //
    // remove effect from effects list
    this.removeEffect = function(effect,            // (BaseFX) effect being removed
                                 giveWarning)       // (boolean) if true warning given if effect not found
    {   
        if (effect === this.effectsDict[effect.name]) {
            
            // have the effect cleanup anything it added
            effect.cleanup();
            
            // Note: cleanup() can change the effects list order
            intIndex = this.effectsList.indexOf(effect);
            
            // delete from effects array and dictionary
            this.effectsList.splice(intIndex, 1);
            delete this.effectsDict[effect.name];
            
            return effect;
            
        } else if (giveWarning) {
            
            // otherwise give warning
            LogAs.warning("Could not remove effect \"" + effect.name + "\" on " + this.target + ".\r" +
                    "Effects that were found were the following: " + dumpEffects() + ".", Animatem.WARNING_CANT_REMOVE_EFFECT);
        }
    }
    
    this.dumpEffects = function() 
    {   
        var nEffects = this.effectsList.length;
        if (nEffects == 0) {
            return "[ none ]";
        }
            
        // iterate through effects
        var str = "";
        for (var i = 0; i < nEffects; ++i) {
                
            if (i == 0) { str += "[ "; }
            str += this.effectsList[i];
            str += (i == (nEffects - 1)) ? " ]" : ", ";
        }
            
        return str;
    }
}

// constants used when setting effect name
Anime.REPLACE = "replace";
Anime.AUTO_RENAME = "rename";
